{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modInteractionAuteur;

interface

uses Classes, LbCipher, LbClass, LbAsym, LbRSA,
     SysUtils, ZLibEx, DCPidea, Windows,
     sFonctionsChaines,
     modConstantes,

     {$IFDEF VERADMIN}
        modInteractionAuthAdmin,
     {$ENDIF}

     DCPblockciphers, DCPcrypt2;


const
   CNT_IDEA_KEY_SIZE = 64;
   CNT_IDEA_KEY_SIZE_CRYPTED_BY_RSA = CNT_IDEA_KEY_SIZE * 2;

type
    TFichierLicenceHeader = record
       Version    : integer;
       Date       : int64;
       File1Size  : int64;
       FileMd5Size: int64;
       TotalSize  : int64;

       ImmediateLeave: integer;
       
       ValueInt  : array[0..9] of integer;
       ValueInt64: array[0..9] of integer;
       ValueBool : array[0..35] of byte;
    end;



const
    CNT_LICENCE_ACCESS_NO_LIMIT = 1;
    CNT_LICENCE_ACCESS_OFFICIER = 2;


    
const
   // petite protection extremement facilement contournable
   CNT_PUBLICKEY_PASSPHRASE = 'non à la pub abusive';


   function iaEncode64URL(S: string): string;
   
   {$IFDEF VERADMIN}
      function iaEncode64(S: string): string;
      function iaDecode64(S: string): string;
   {$ENDIF}

   function iaEncode64s(St: TStream): string;
   function iaEncode64ss(St: TStream; Dst: TStream): boolean;

   function iaDecode64s(S: string; St: TStream): boolean;


   function iaCreateChunkOf64Char(s: string): string;
   function iaRemoveChunk(s: string): string;




   // Décrypte avec RSA + IDEA
   // Entrée: la chaine en base64
   // Sortie la chaine en clair
   {$IFDEF VERADMIN}
   function iaRSADecryptage(aStr: string; KeyFile: string; KeyPassPhrase: string): string;
   {$ENDIF}
   function iaRSADecryptageKeyStream(aStr: string; KeyFileXStream: TStream; KeyPassPhrase: string): string;


   // Crypte avec RSA + IDEA
   // Entrée: La chaine en clair
   // Sortie: La chaine en base64
   {$IFDEF VERADMIN}
   function iaRSACryptageChaine(sStr: string; KeyFile: string; KeyPassPhrase: string): string;
   {$ENDIF}
   function iaRSACryptageChaineKeyStream(sStr: string; KeyFileXStream: TStream; KeyPassPhrase: string): string;



   // generation d'une clé pour IDEA
   function iaGenerateKeyForIDEA(GenerationHelp: string = ''): string;


   // Lors du postage des données, on utilise un TStream pour les données postées
   function iaIDEACrypteThisString(sMsg: string; const Key; KeySize: longword; Dest: TStream): integer;

   // La procédure de décryptage doit exister car il faudra bien décrypté les news
   function iaIDEADecryptThisString(sCrypted: TStream; const Key; KeySize: longword): string;


   // Crypte seulement sans compressé, utilisé pour le cryptage de chaine courte
   // comme par exemple le titre d'un topic
   function iaIDEACrypteOnlyThisString(sMsg: string; const Key; KeySize: longword; Dest: TStream): integer;
   function iaIDEADecryptOnlyThisString(sCrypted: TStream; const Key; KeySize: longword): string;

   // Irréversible si le mot de passe est assez long, ou si la base est négative
   function Crypto_AlgoIrreversible1_Codage(const Chaine: string): int64;

   

   function iaExtractDataBetweenBeginEnd(var str: string): boolean;


   procedure iaInitRSA;



{$IFDEF VERADMIN}
   // Construit un fichier de licence
   function BuildLicenceKey(Fichier: string; Confidentiel, KeyPublicFile: string; VerifData, KeyPrivateFile: string): integer;

   // Extrait les données confidentielles d'un fichier licence
   function GetLicenceKeyData(Fichier: string; KeyPrivateFile, KeyPublicFile: string): string;

{$ENDIF}


var
    cryptRSA : TLbRSA;
    cryptIDEA: TDCP_idea;


implementation

uses modMd5;


function Crypto_AlgoIrreversible1_Codage(const Chaine: string): int64;
//const Base = -98514;
var Sum: int64;
    j : longword;
    lng: integer;
begin

     Sum := -98514;

     if length(Chaine) = 0 then begin
        Result := Sum;
        exit;
     end;

     // Evite d'utiliser la fonction Length à chaque itération de la boucle
     lng := length(Chaine);


     for j := 1 to lng do
        Sum := Sum + (ord(Chaine[j]) * Sum) + ord(Chaine[j]);


     Result := Sum;
end;

function iaExtractDataBetweenBeginEnd(var str: string): boolean;
var xPos: integer;
begin

    Result := false;

    // avec les putains de pub de l'hébergeur gratuit, il se peut que le texte ne contienne pas
    // uniquement du code en base64
    // un BEGIN et END ont donc été ajouté
    xPos := pos(CNT_RSA_BLOCK_BEGIN, str);

    if xPos < 1 then begin
       // page invalide
       exit;
    end;

    System.Delete(str, 1, xPos);
    xPos := pos(#$0A, str);
    if xPos < 2 then begin
       exit;
    end;

    System.Delete(str, 1, xPos);
    str := trimleft(str);



    xPos := pos(CNT_RSA_BLOCK_END, str);
    if xPos < 1 then begin
        exit;
    end;

    System.Delete(str, xPos, maxint);

    // maintenant recherche le dernier fin de ligne
    xPos := LastDelimiter(#$0A#$0D, str);

    if xPos < 1 then begin
       exit;
    end;

    System.Delete(str, xPos, maxint);
    str := trimright(str);

    Result := true;

end;

function iaCreateChunkOf64Char(s: string): string;
var i: integer;
begin

    Result := '';

    repeat
       Result := Result + copy(s, 1, 64) + #$0D#$0A;
       System.Delete(s, 1, 64);
    until s = '';


end;


function iaRemoveChunk(s: string): string;
begin
    Result := sfcRemplace(s, #$0A, '');
    Result := sfcRemplace(Result, #$0D, '');

end;

{$IFDEF VERADMIN}
function iaRSACryptageChaine(sStr: string; KeyFile: string; KeyPassPhrase: string): string;
var KeyFileStream : TFileStream;
begin

    Result := '';
    try
       KeyFileStream := TFileStream.Create(KeyFile, fmOpenRead);
    except
       KeyFileStream := nil;
    end;

    if KeyFileStream = nil then begin
       // echec
       exit;
    end;

    Result := iaRSACryptageChaineKeyStream(sStr, KeyFileStream, KeyPassPhrase);

    KeyFileStream.Free;

end;
{$ENDIF}


function iaRSACryptageChaineKeyStream(sStr: string; KeyFileXStream: TStream; KeyPassPhrase: string): string;
var IDEAKey: string;
    sIn, sOut, sFinalStream: TMemoryStream;
begin

     // génére une clé idea
     IDEAKey := iaGenerateKeyForIDEA;

     // On place la clé dans le stream
     sIn := TMemoryStream.Create;
     sIn.Write(IDEAKey[1], length(IDEAKey));
     sIn.Seek(0, soBeginning);


     // la clé est trop petite pour pouvoir la compresser
     // on obtient un stream de 75 octets en compressant alors qu'à l'origine
     // la clé fait 64 octets


     // on chiffre la clé avec RSA dans sOut
     sOut := TMemoryStream.Create;

     iaInitRSA;

     // Le cryptage s'effectue toujours avec la clé publique
     // on charge la clé privé en tant que clé publique
     cryptRSA.PublicKey.Passphrase := KeyPassPhrase;
     cryptRSA.PublicKey.LoadFromStream(KeyFileXStream);


     // crypte la clé
     cryptRSA.EncryptStream(sIn, sOut);
     sIn.Free;
     sIn := nil;

     // la clé fait 64 octets, et fait une fois chiffré 128 octets (2*64)
     // cette taille sera toujours la même, inutile de la mémoriser


     // s'assure que l'on est à la fin du stream
     sOut.Seek(0, soEnd);

     // rajoute à ce stream crypté la chaine que l'on veut crypter
     if iaIDEACrypteThisString(sStr, IDEAKey[1], CNT_IDEA_KEY_SIZE, sOut) < 0 then begin
        // erreur...
        
     end;

     // détruit le contenu de la clé
     FillChar(IDEAKey[1], CNT_IDEA_KEY_SIZE, #0);


     // dans sOut il y a désormais la clé IDEA crypté avec RSA et
     // notre chaine crypté par IDEA
     // il ne reste plus qu'à la convertir en base64
     sOut.Seek(0, soBeginning);
     Result := iaEncode64s(sOut);

     sOut.Free;
end;

{$IFDEF VERADMIN}
function iaRSADecryptage(aStr: string; KeyFile: string; KeyPassPhrase: string): string;
var KeyFileStream : TFileStream;
begin

    Result := '';
    try
       KeyFileStream := TFileStream.Create(KeyFile, fmOpenRead);
    except
       KeyFileStream := nil;
    end;

    if KeyFileStream = nil then begin
       // echec
       exit;
    end;

    Result := iaRSADecryptageKeyStream(aStr, KeyFileStream, KeyPassPhrase);

    KeyFileStream.Free;

end;
{$ENDIF}

function iaRSADecryptageKeyStream(aStr: string; KeyFileXStream: TStream; KeyPassPhrase: string): string;
var IDEAKey: string;
    sIn, sOut, sInKey: TMemoryStream;

    bError: boolean;
begin

     iaInitRSA;
     Result := '';

     aStr := iaRemoveChunk(aStr);
     

     // On place les données dans le stream
     sIn := TMemoryStream.Create;
     iaDecode64s(aStr, sIn);
     sIn.Seek(0, soBeginning);
     

     // la clé est crypté sur les 128 premier octets du stream d'entrée
     if sIn.Size < CNT_IDEA_KEY_SIZE_CRYPTED_BY_RSA then begin
        // stream invalide
        sIn.Free;
        exit;

     end else if sIn.Size = CNT_IDEA_KEY_SIZE_CRYPTED_BY_RSA then begin
        // aucune donnée de cryptée
        sIn.Free;
        exit;
     end;



     // on décrypte la clé avec RSA dans sOut
     sOut   := TMemoryStream.Create;
     sInKey := TMemoryStream.Create;

     sInKey.CopyFrom(sIn, CNT_IDEA_KEY_SIZE_CRYPTED_BY_RSA);
     sInKey.Seek(0, soBeginning);

     // Le cryptage s'effectue toujours avec la clé publique
     // on charge la clé privé en tant que clé publique
     cryptRSA.PrivateKey.Passphrase := KeyPassPhrase;
     cryptRSA.PrivateKey.LoadFromStream(KeyFileXStream);


     bError := false;
     try
        cryptRSA.DecryptStream(sInKey, sOut);
     except
        bError := true;
     end;

     sInKey.Free;

     if bError then begin
        // Erreur de decryptage de la cle avec RSA
        sOut.Free;
        sIn.Free;
        Result := '';
        exit;
     end;


     // la clé est maintenant décrypté dans sOut
     sOut.Seek(0, soBeginning);
     if sOut.Size <> CNT_IDEA_KEY_SIZE then begin
        // erreur de taille de la clé
        sOut.Free;
        sIn.Free;
        exit;
     end;

     SetLength(IDEAKey, CNT_IDEA_KEY_SIZE);
     sOut.Read(IDEAKey[1], CNT_IDEA_KEY_SIZE);
     sOut.Free;

     try
       Result := iaIDEADecryptThisString(sIn, IDEAKey[1], CNT_IDEA_KEY_SIZE);
     except
       Result := '';
     end;

     
     // détruit le contenu de la clé
     FillChar(IDEAKey[1], CNT_IDEA_KEY_SIZE, #0);

     sIn.Free;
end;


function iaGenerateKeyForIDEA(GenerationHelp: string = ''): string;
var i, j: integer;
begin

    Randomize;
    j := 1;

    if length(GenerationHelp) < 1 then
       GenerationHelp := #0;


    SetLength(Result, CNT_IDEA_KEY_SIZE);
    for i := 1 to CNT_IDEA_KEY_SIZE do begin
       Result[i] := chr((Random(256) + Ord(GenerationHelp[j]) mod 256));

       j := (GetTickCount mod length(GenerationHelp) + 1);
       if j = 0 then begin
          j := 1;
          Sleep(j);
       end;
    end

end;

procedure InitIdea;
begin
    if cryptIDEA = nil then begin
       cryptIDEA := TDCP_idea.Create(nil);
    end;

    // au cas où les données du composant auraient été modifiée
    cryptIDEA.BlockSize  := 64;
    cryptIDEA.MaxKeySize := 128;
    cryptIDEA.CipherMode := cmCFBblock;
end;

procedure iaInitRSA;
begin
    if cryptRSA = nil then begin
       cryptRSA := TLbRSA.Create(nil);
    end;

    cryptRSA.KeySize := aks1024;
    cryptRSA.PrimeTestIterations := 20;
end;

function iaIDEADecryptOnlyThisString(sCrypted: TStream; const Key; KeySize: longword): string;
var sDecrypted: TMemoryStream;
begin
    Result := '';


    // décrypte (dans sDecrypted)
    sDecrypted := TMemoryStream.Create;


    InitIdea;
    cryptIDEA.Init(Key, KeySize, nil);
    cryptIDEA.DecryptStream(sCrypted, sDecrypted, sCrypted.Size - sCrypted.Position);
    cryptIDEA.Burn;



    // place dans une chaine
    sDecrypted.Seek(0, soBeginning);
    SetLength(Result, sDecrypted.Size);
    if sDecrypted.Size > 0 then
       sDecrypted.Read(Result[1], sDecrypted.Size)
    else
       Result := '';


    sDecrypted.Free;
end;


// décrypte -> déCompresse -> resultat en string (chaine en claire)
function iaIDEADecryptThisString(sCrypted: TStream; const Key; KeySize: longword): string;
var sDecrypted, sDecryptedCompressed: TMemoryStream;
    DeCompressionStream: TZDecompressionStream;
begin

    Result := '';



    // décrypte (dans sDecryptedCompressed)
    sDecryptedCompressed := TMemoryStream.Create;



    InitIdea;
    cryptIDEA.Init(Key, KeySize, nil);
    cryptIDEA.DecryptStream(sCrypted, sDecryptedCompressed, sCrypted.Size - sCrypted.Position);
    cryptIDEA.Burn;



    // décompresse (dans sDecrypted)
    sDecrypted := TMemoryStream.Create;

    DeCompressionStream := TZDecompressionStream.Create(sDecryptedCompressed);
    sDecrypted.CopyFrom(DecompressionStream, 0);
    DecompressionStream.Free;
    sDecryptedCompressed.Free;


    // place dans une chaine
    sDecrypted.Seek(0, soBeginning);
    SetLength(Result, sDecrypted.Size);
    if sDecrypted.Size > 0 then
       sDecrypted.Read(Result[1], sDecrypted.Size)
    else
       Result := '';


    sDecrypted.Free;

end;

function iaIDEACrypteOnlyThisString(sMsg: string; const Key; KeySize: longword; Dest: TStream): integer;
var sIn: TMemoryStream;
begin
    Result := 1;

    try
       sIn := TMemoryStream.Create;
       sIn.Write(sMsg[1], length(sMsg));
       sIn.Seek(0, soBeginning);
    except
       Result := -1;
    end;

    if Result < 1 then begin
       sIn.Free;
       exit;
    end;

    // crypte les données
    try
       InitIdea;
       cryptIDEA.Init(Key, KeySize, nil);
       cryptIDEA.EncryptStream(sIn, Dest, sIn.Size);
       cryptIDEA.Burn;
    except
       Result := -1;
    end;

    sIn.Free;

end;

// Compresse -> crypte 
function iaIDEACrypteThisString(sMsg: string; const Key; KeySize: longword; Dest: TStream): integer;
var sIn, sInCompress: TMemoryStream;
    CompressionStream: TZCompressionStream;
begin


    Result := 1;

    try
       sIn := TMemoryStream.Create;
       sIn.Write(sMsg[1], length(sMsg));
       sIn.Seek(0, soBeginning);
    except
       Result := -1;
    end;

    if Result < 1 then begin
       sIn.Free;
       exit;
    end;


    // compresse les données avant de les chiffrer
    // les compresser après serait inutile car tout le monde pourrait
    // les décompresser
    try
       sInCompress := TMemoryStream.Create;
       CompressionStream := TZCompressionStream.Create(sInCompress, zcMax);
       CompressionStream.CopyFrom(sIn, sIn.Size);
    except
       Result := -1;
    end;
    CompressionStream.Free;
    sIn.Free;

    if Result < 1 then begin
       sInCompress.Free;
       exit;
    end;

    sInCompress.Seek(0, soBeginning);



    // chiffre les données compressées
    try
       InitIdea;
       cryptIDEA.Init(Key, KeySize, nil);
       cryptIDEA.EncryptStream(sInCompress, Dest, sInCompress.Size);
       cryptIDEA.Burn;
    except
       Result := -1;
    end;

    sInCompress.Free;


    // converti en base 64
    // Edit: La conversion en b64 s'effectue à la fin, en même temps
    //       qu'avec la clé crypté par RSA
    //sCrypted.Seek(0, soBeginning);
    //iaEncode64ss(sCrypted, Dest);

    // libération de la mémoire
    //sCrypted.Free;
end;



const
  Codes64     = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/';

const
  SZCodes64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';



function iaDecode64s(S: string; St: TStream): boolean;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;

begin


  Result := true;
  a := 0;
  b := 0;
  for i := 1 to Length(s) do begin
    x := Pos(s[i], codes64) - 1;
    if x >= 0 then begin
      b := b * 64 + x;
      a := a + 6;
      if a >= 8 then begin
        a := a - 8;
        x := b shr a;
        b := b mod (1 shl a);
        x := x mod 256;

        // Result := Result + chr(x);
        St.Write(x, 1);

      end;
    end else begin
      Exit;
    end;

  end;

  Result := true;
end;

function iaEncode64ss(St: TStream; Dst: TStream): boolean;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
  c: char;

  NbData: integer;
begin
  Result := true;
  a := 0;
  b := 0;
  x := 0;

  NbData := St.Size - St.Position;

  for i := 1 to NbData do begin
    St.Read(x, 1);
    //x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);

      c := Codes64[x + 1];
      Dst.Write(c, 1);
    end;
  end;

  if a > 0 then begin
    x := b shl (6 - a);
    c := Codes64[x + 1];
    Dst.Write(c, 1);
  end;

  
end;

function iaEncode64s(St: TStream): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;

  NbData: integer;
begin
  Result := '';
  a := 0;
  b := 0;
  x := 0;

  NbData := St.Size - St.Position;

  for i := 1 to NbData do begin
    St.Read(x, 1);
    //x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);
      Result := Result + Codes64[x + 1];
    end;
  end;

  if a > 0 then begin
    x := b shl (6 - a);
    Result := Result + Codes64[x + 1];
  end;

end;


function iaEncode64URL(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do begin
    x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);
      Result := Result + SZCodes64URL[x + 1];
    end;
  end;

  if a > 0 then begin
    x := b shl (6 - a);
    Result := Result + SZCodes64URL[x + 1];
  end;
end;

{$IFDEF VERADMIN}
function iaEncode64(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do begin
    x := Ord(s[i]);
    b := b * 256 + x;
    a := a + 8;
    while a >= 6 do begin
      a := a - 6;
      x := b div (1 shl a);
      b := b mod (1 shl a);
      Result := Result + Codes64[x + 1];
    end;
  end;

  if a > 0 then begin
    x := b shl (6 - a);
    Result := Result + Codes64[x + 1];
  end;
end;


function iaDecode64(S: string): string;
var
  i: Integer;
  a: Integer;
  x: Integer;
  b: Integer;
begin
  Result := '';
  a := 0;
  b := 0;
  for i := 1 to Length(s) do begin
    x := Pos(s[i], codes64) - 1;
    if x >= 0 then begin
      b := b * 64 + x;
      a := a + 6;
      if a >= 8 then begin
        a := a - 8;
        x := b shr a;
        b := b mod (1 shl a);
        x := x mod 256;
        Result := Result + chr(x);
      end;

    end else
      Exit;

  end;
end;
{$ENDIF}



{$IFDEF VERADMIN}


function GetLicenceKeyDataFromStream(FIn: TStream; KeyPrivateFile, KeyPublicFile: string): string;
label lExit;
var md5Calculer: TMd5HashInfo;
    md5In: TMd5HashInfo;
    iCheck: integer;

    iLeave       : integer;
    tmpStream    : TMemoryStream;
    sMd5Crypted  : string;
    sMd5Decrypted: string;

    DeCompressionStream: TZDecompressionStream;

    InDecompressed: TMemoryStream;

    FHeader: TFichierLicenceHeader;

    StreamDonneesInteressante: TMemoryStream;
begin


    iLeave := 0;
    Result := '';


    if FIn = nil then begin
       exit;
    end;



    // décompresse
    InDecompressed := TMemoryStream.Create;


    FIn.Read(iCheck, SizeOf(Integer));

    if (iCheck and 1) = 1 then begin
       // nombre impair
       exit;
    end;

    if FIn.Size < 32 then begin
       exit;
    end;

    try
       DeCompressionStream := TZDecompressionStream.Create(FIn);
       InDecompressed.CopyFrom(DecompressionStream, FIn.Size - SizeOf(Integer));
       DecompressionStream.Free;
       DecompressionStream := nil;

       FIn.Seek(0, soBeginning);

       InDecompressed.Seek(0, soBeginning);
    except
       iLeave := 1;
    end;

    if iLeave = 1 then begin
       if DecompressionStream <> nil then begin
          DecompressionStream.Free;
       end;
       InDecompressed.Free;
       exit;
    end;

    if InDecompressed.Read(FHeader, SizeOf(FHeader)) < SizeOf(FHeader) then begin
       InDecompressed.Free;
       exit;
    end;

    // recommence la décompression avec la bonne taille

    FIn.Position := SizeOf(Integer);
    InDecompressed.Free;
    InDecompressed := TMemoryStream.Create;

    try
       DeCompressionStream := TZDecompressionStream.Create(FIn);
       InDecompressed.CopyFrom(DecompressionStream, FHeader.TotalSize);
       DecompressionStream.Free;
       DecompressionStream := nil;

       FIn.Seek(0, soBeginning);

       InDecompressed.Seek(0, soBeginning);
    except
       iLeave := 1;
    end;

    if iLeave = 1 then begin
       if DecompressionStream <> nil then begin
          DecompressionStream.Free;
       end;
       InDecompressed.Free;
       exit;
    end;


    // Relit l'header
    if InDecompressed.Read(FHeader, SizeOf(FHeader)) < SizeOf(FHeader) then begin
       InDecompressed.Free;
       exit;
    end;

    if InDecompressed.Size <> FHeader.TotalSize then begin
       InDecompressed.Free;
       exit;
    end;


    StreamDonneesInteressante := TMemoryStream.Create;
    StreamDonneesInteressante.CopyFrom(InDecompressed, FHeader.File1Size);
    StreamDonneesInteressante.Seek(0, soBeginning);

    md5Calculer := md5Stream(StreamDonneesInteressante, FHeader.File1Size);

    StreamDonneesInteressante.Seek(0, soBeginning);

    
    // Le champ ImmediateLeave fait quitter tout de suite
    // mais on laisse faire le calcul md5 avant
    if FHeader.ImmediateLeave > 0 then begin
lExit:
       InDecompressed.Free;
       exit;
    end;


    tmpStream := TMemoryStream.Create;

    // Lit le hash md5 crypté
    tmpStream.CopyFrom(InDecompressed, FHeader.FileMd5Size);
    tmpStream.Seek(0, soBeginning);

    sMd5Crypted   := iaEncode64s(tmpStream);
    tmpStream.Free;
    tmpStream := nil;

    // dechiffre le md5 (mais sera sous forme binaire dans le string)
    sMd5Decrypted := iaRSADecryptage(sMd5Crypted, KeyPublicFile, CNT_PUBLICKEY_PASSPHRASE);

    if length(sMd5Decrypted) < SizeOf(md5In) then begin
       InDecompressed.Free;
       exit;
    end;

    Move(sMd5Decrypted[1], md5In, SizeOf(md5In));


    if md5IsEgal(md5In, md5Calculer) = false then begin
       // Definit un nombre impair
       FHeader.ImmediateLeave := (iCheck + md5Somme(md5In)) or 1;

       // L'écrit dans le debut du fichier
       FIn.Write(FHeader.ImmediateLeave, SizeOf(FHeader.ImmediateLeave));
       FIn.Seek(64, soBeginning);
    end;

    InDecompressed.Free;


    StreamDonneesInteressante.Seek(0, soBeginning);

    Result := iaEncode64s(StreamDonneesInteressante);
    

    Result := iaRSADecryptage(Result, KeyPrivateFile, CNT_PRIVATEKEY_PASSPHRASE);


end;

function GetLicenceKeyData(Fichier: string; KeyPrivateFile, KeyPublicFile: string): string;
var FIn: TFileStream;
begin


    Result := '';
    
    if FileExists(Fichier) = false then begin
       exit;
    end;

    try
       FIn := TFileStream.Create(Fichier, fmOpenReadWrite);
    except
       FIn := nil;
    end;

    if FIn = nil then begin
       exit;
    end;

    Result := GetLicenceKeyDataFromStream(FIn, KeyPrivateFile, KeyPublicFile);

    FIn.Free;

end;


function BuildLicenceKey(Fichier: string; Confidentiel, KeyPublicFile: string; VerifData, KeyPrivateFile: string): integer;
var sConfidentiel, sNonConfidentiel: string;
    md5: TMd5HashInfo;
    Header: TFichierLicenceHeader;
    FOut: TFileStream;

    PositionComeBack: int64;
    iRnd: integer;

    sMd5, sMd5Crypted: string;


    InStream: TMemoryStream;
    CompressionStream: TZCompressionStream;

begin
(*****************************************
 Structure du fichier:

 header|fichier1|md5crypté|fichier2

 Le fichier 2 est inutile à vérifier par md5 puisqu'il est généré par ma clé
 privé, il est forcément généré par moi si il est valide
 *)


    try
       FOut := TFileStream.Create(Fichier, fmCreate);
    except
       FOut := nil;
    end;

    if FOut = nil then begin
       Result := -1;
       exit;
    end;

    InStream := TMemoryStream.Create;

    sConfidentiel    := iaRSACryptageChaine(Confidentiel, KeyPublicFile, CNT_PUBLICKEY_PASSPHRASE);
    sNonConfidentiel := iaRSACryptageChaine(VerifData, KeyPrivateFile, CNT_PRIVATEKEY_PASSPHRASE);




    FillChar(Header, SizeOf(Header), 0);
    Header.Version := 1;
    InStream.Write(Header, SizeOf(Header));

    // Ecrit le fichier confidentiel
    PositionComeBack := InStream.Position;
    iaDecode64s(sConfidentiel, InStream);

    Header.File1Size := InStream.Position - PositionComeBack;


    // Calcul le md5 du fichier confidentiel
    InStream.Seek(PositionComeBack, soBeginning);
    md5 := md5Stream(InStream, Header.File1Size);

    // Place le hash dans une chaine pour pouvoir le chiffrer
    SetLength(sMd5, SizeOf(md5));
    Move(md5, sMd5[1], SizeOf(md5));

    // Le md5 doit pouvoir etre déchiffré, il est donc chiffré avec ma clé
    // privé
    sMd5Crypted := iaRSACryptageChaine(sMd5, KeyPrivateFile, CNT_PRIVATEKEY_PASSPHRASE);


    PositionComeBack := InStream.Position;

    // s'assure d'etre bien a la fin et ecrit le md5
    InStream.Seek(0, soEnd);
    iaDecode64s(sMd5Crypted, InStream);

    HEader.FileMd5Size := InStream.Position - PositionComeBack;


    PositionComeBack := InStream.Position;

    // Ecriture fichier non condidentiel...
    iaDecode64s(sNonConfidentiel, InStream);

    
    Header.TotalSize := InStream.Position;

    // Met à jour l'header
    InStream.Seek(0, soBeginning);
    InStream.Write(Header, SizeOf(Header));



    // Ecrit une donnée aléatoire
    Randomize;
    iRnd := Random(MAXINT);
    Sleep(4);
    iRnd := Random(MAXINT);

    // force un nombre pair
    iRnd := iRnd or 1;
    iRnd := iRnd xor 1;

    FOut.Write(iRnd, SizeOf(Integer));

    InStream.Seek(0, soBeginning);

    try
       CompressionStream := TZCompressionStream.Create(FOut, zcMax);
       CompressionStream.CopyFrom(InStream, InStream.Size);
    except
       FOut.Free;
       FOut := nil;
    end;

    CompressionStream.Free;
    InStream.Free;

    FOut.Free;
end;
{$ENDIF}




initialization
    // s'assure que les pointeurs sont à nil
    cryptRSA := nil;
    cryptIDEA:= nil;

finalization
    if cryptRSA <> nil then begin
       cryptRSA.Free;
       cryptRSA := nil;
    end;

    if cryptIDEA <> nil then begin
       cryptIDEA.Burn;
       cryptIDEA.Free;
       cryptIDEA := nil;
    end;

end.
